#include "windows_adapters_retriever.h"

#include <winsock2.h>
#include <iphlpapi.h>

#pragma comment(lib, "IPHLPAPI.lib")

#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

windows_adapters_info::windows_adapters_info()
	: m_str_adapter_name(0)
	, m_str_adapter_description(0)
	, m_str_adapter_friendlyname(0)
{
	;
}

windows_adapters_info::~windows_adapters_info()
{
	delete[]m_str_adapter_name;
	delete[]m_str_adapter_description;
	delete[]m_str_adapter_friendlyname;
}

void windows_adapters_info::set_adapter_name(char * adapter_name)
{
	cpy_string(&m_str_adapter_name, adapter_name);
}

void windows_adapters_info::set_adapter_description(char * adapter_description)
{
	cpy_string(&m_str_adapter_description, adapter_description);
}

void windows_adapters_info::set_adapter_friendlyname(char * adapter_friendlyname)
{
	cpy_string(&m_str_adapter_friendlyname, adapter_friendlyname);
}

char * windows_adapters_info::get_adapter_name()
{
	return m_str_adapter_name;
}

char * windows_adapters_info::get_adapter_description()
{
	return m_str_adapter_description;
}

char * windows_adapters_info::get_adapter_friendlyname()
{
	return m_str_adapter_friendlyname;
}

void windows_adapters_info::cpy_string(char ** ptr_str_dest, char * str_src)
{
	delete[] * ptr_str_dest;

	int len = strlen(str_src);

	*ptr_str_dest = new char[len + 1];
	memset(*ptr_str_dest, 0, len + 1);

	strcpy_s(*ptr_str_dest, len + 1, str_src);
}

windows_adapters_retriever::windows_adapters_retriever()
{
}


windows_adapters_retriever::~windows_adapters_retriever()
{
}

bool windows_adapters_retriever::get_adapter_list(std::vector< windows_adapters_info* >& info)
{
	DWORD dwRetVal = 0;

	unsigned int i = 0;

	ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES;

	ULONG family = AF_INET;

	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
	ULONG outBufLen = 0;
	ULONG Iterations = 0;

	PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;

	outBufLen = WORKING_BUFFER_SIZE;

	do 
	{
		pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC(outBufLen);
		if (pAddresses == NULL)
		{
			return false;
		}

		dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

		if (dwRetVal == ERROR_BUFFER_OVERFLOW)
		{
			FREE(pAddresses);
			pAddresses = NULL;
		}
		else
			break;

		Iterations++;

	} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	if (dwRetVal == NO_ERROR)
	{
		pCurrAddresses = pAddresses;
		while (pCurrAddresses)
		{
			windows_adapters_info * ptr_info = new windows_adapters_info();
			ptr_info->set_adapter_name(pCurrAddresses->AdapterName);

			int size = wcslen(pCurrAddresses->Description);
			char * ptr_temp = new char[size * 3 + 1];
			memset(ptr_temp, 0, size * 3 + 1);
			WideCharToMultiByte(CP_ACP, 0, pCurrAddresses->Description, size, ptr_temp, size * 3 + 1, 0, 0);

			ptr_info->set_adapter_description(ptr_temp);
			delete[]ptr_temp;

			size = wcslen(pCurrAddresses->FriendlyName);
			ptr_temp = new char[size * 3 + 1];
			memset(ptr_temp, 0, size * 3 + 1);
			WideCharToMultiByte(CP_ACP, 0, pCurrAddresses->FriendlyName, size, ptr_temp, size * 3 + 1, 0, 0);
			ptr_info->set_adapter_friendlyname(ptr_temp);
			delete[]ptr_temp;

			info.push_back(ptr_info);

			pCurrAddresses = pCurrAddresses->Next;
		}
	}

	if (pAddresses)
	{
		FREE(pAddresses);
	}

	if (dwRetVal == NO_ERROR)
		return true;
	else
		return false;
}
